﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;
using Plugins.Core;
using Plugins.Core.Interfaces;
using Plugins.Engine;
using Plugins.Interfaces;
using Plugins.Library;

namespace Plugins.Controls
{
    [Serializable]
    [PluginName("Plugin window")]
    [PluginAuthor("hack2root")]
    [PluginDescription("Plugin shows the graphical user interface window")]
    public partial class PluginWindow :
        Form,
        IPluginWindow,
        IPluginWindowHost,
        IPluginConnector<IPluginOptions>,
        IPluginConnector<IPluginWindowHost>,
        IPluginExecuteConnector<IPluginOptions>,
        IPluginExecuteConnector<IPluginWindowHost>
    {
        private IPluginOptions _options;
        private BackgroundProperty _backgroundProperty;
        private IPluginOptions _defaultOptions = new PluginOptions();
        private IPluginWindowManager _windowManager = new PluginWindowManger();
        public IPluginOptions DefaultOptions { get { return _defaultOptions; } }
        public IPluginWindowManager WindowManager { get { return _windowManager; } }
        public IPluginExecuteConnector<IPluginOptions> Options { get { return this; } }
        public IPluginExecuteConnector<IPluginWindowHost> Host { get { return this; } }

        [Category("My")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public BackgroundProperty Background
        {
            get
            {
                return _backgroundProperty;
            }
        }

        public Control Window { get { return this; } }

        public PluginGradientControl BackgroundControl { get { return pluginGradientControl1; } }

        public void Popup(IPluginWindowHost host, IPluginWindow window)
        {
            if (!_windowManager.ContainsActive(window))
            {
                window.Attach(host);
                Options.Execute(options => window.Attach(options));
                window.OpenWindow(this, false, false);
            }
            else
            {
                window.CloseWindow();
                Options.Execute(options => window.Detach(options));
                window.Detach(host);
            }
        }

        public void LoadPopupWindows()
        {
            _windowManager.Execute(window => window.Load());
            IEnumerable<string> windowNames = GetAutoLoadWindowsList();
            if (windowNames != null)
            {
                AutoLoadWindows(windowNames);
            }
            else
            {
                Options.Execute(options => options.Items.Add("popupWindows"));
            }
        }

        public void UnloadPopupWindows()
        {
            _windowManager.Execute(window => window.Save());
            IEnumerable<string> windowNames = GetAutoSaveWindowsList();
            if (windowNames != null)
            {
                AutoSaveWindows(windowNames);
            }
            else
            {
                Options.Execute(options => options.Items.Remove("popupWindows"));
            }
        }

        public IEnumerable<string> GetAutoLoadWindowsList()
        {
            List<string> result = null;
            Options.Execute(options => result = options.Items.GetValue<List<string>>("popupWindows"));
            return result;
        }

        public IEnumerable<string> GetAutoSaveWindowsList()
        {
            return _windowManager.ActiveWindows;
        }

        private void AutoLoadWindows(IEnumerable<string> windowNames)
        {
            List<string> windows = new List<string>(windowNames);
            windows.ForEach(windowName => Popup(this, _windowManager.GetWindow(windowName)));
        }

        private void AutoSaveWindows(IEnumerable<string> windowNames)
        {
            List<string> windows = new List<string>(windowNames);
            Options.Execute(options => options.Items.SetValue<List<string>>("popupWindows", windows, windows));
        }

        bool IPluginExecuteConnector<IPluginOptions>.Execute(Action<IPluginOptions> action)
        {
            if (_options != null)
            {
                if (action != null)
                {
                    action(_options);
                }
                return true;
            }
            return false;
        }
        bool IPluginExecuteConnector<IPluginWindowHost>.Execute(Action<IPluginWindowHost> action)
        {
            if (_host != null)
            {
                if (action != null)
                {
                    action(_host);
                }
                return true;
            }
            return false;
        }
        bool IPluginConnector<IPluginOptions>.Attach(IPluginOptions options)
        {
            if (_options != options && _options == null)
            {
                _options = options;
                return true;
            }
            return true;
        }
        bool IPluginConnector<IPluginOptions>.Detach(IPluginOptions options)
        {
            if (_options == options && _options != null)
            {
                _options = null;
                return true;
            }
            return false;
        }
        bool IPluginConnector<IPluginWindowHost>.Attach(IPluginWindowHost host)
        {
            if (_host != host && _host == null)
            {
                _host = host;
                return true;
            }
            return true;
        }
        bool IPluginConnector<IPluginWindowHost>.Detach(IPluginWindowHost host)
        {
            if (_host == host && _host != null)
            {
                _host = host;
                return true;
            }
            return true;
        }
        protected void LoadOptions(string fileName, IPluginOptions options)
        {
            IPluginWindow window = this;
            window.Attach(options);
            options.Load(fileName);
        }
        protected void UnloadOptions(string fileName, IPluginOptions options)
        {
            IPluginWindow window = this;
            options.Save(fileName);
            window.Detach(options);
        }
        protected void LoadOptions(string fileName)
        {
            LoadOptions(fileName, DefaultOptions);
        }
        protected void UnloadOptions(string fileName)
        {
            UnloadOptions(fileName, DefaultOptions);
        }

        private IPluginWindowHost _host;

        private PluginWindowState _windowState;
        private static IPluginPropertyConverter<FormWindowState> _converter = new PluginWindowStatePropertyConverter();

        public PluginWindowState State { get { return _windowState; } }

        private bool _showToolStrip = false;
        private bool _showStatusStrip = false;

        private IPluginWindowItem _toolPanel;
        private IPluginWindowItem _statusPanel;

        public event EventHandler<PluginWindowEventArgs> NotifyOpen;
        public event EventHandler<PluginWindowEventArgs> NotifyClose;

        protected void LoadWindowState()
        {
            this._windowState = GetWindowState(this.Name);
            if (!_isWindowResizing)
            {
                UpdateWindowState();
            }
        }
        protected void UnloadWindowState()
        {
            SetWindowState(this.Name);
        }

        public bool IsReadOnly { get; set; }

        private void UpdateWindowState()
        {
            if (!this.IsReadOnly)
            {
                this.SuspendLayout();
                this.StartPosition = FormStartPosition.Manual;
                this.Location = this.State.Location;
                this.Size = this.State.Size;
                this.ResumeLayout(false);
                this.WindowState = this.State.WindowState;
            }
        }

        void IPluginEntity.Load()
        {
            LoadWindowState();
        }
        void IPluginEntity.Save()
        {
            UnloadWindowState();
        }

        private PluginWindowState GetWindowState(string windowName)
        {
            PluginWindowState windowState = new PluginWindowState(new Point(this.Left, this.Top), new Size(this.Width, this.Height), this.WindowState);
            IPluginExecuteConnector<IPluginOptions> connector = this;
            connector.Execute(options =>
            {
                int left = options.Items.GetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowLeft", windowName), this.Left);
                int top = options.Items.GetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowTop", windowName), this.Top);
                int width = options.Items.GetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowWidth", windowName), this.Width);
                int height = options.Items.GetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowHeight", windowName), this.Height);
                FormWindowState state = options.Items.GetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowState", windowName), FormWindowState.Normal, _converter);
                windowState = new PluginWindowState(new Point(left, top), new Size(width, height), state);
            });
            return windowState;
        }
        private void SetWindowState(string windowName)
        {
            PluginWindowState windowState = new PluginWindowState(new Point(this.Left, this.Top), new Size(this.Width, this.Height), this.WindowState);
            IPluginExecuteConnector<IPluginOptions> connector = this;
            connector.Execute(options =>
            {
                if (windowState.WindowState == FormWindowState.Normal)
                {
                    options.Items.SetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowLeft", windowName), windowState.Location.X, windowState.Location.X);
                    options.Items.SetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowTop", windowName), windowState.Location.Y, windowState.Location.Y);
                    options.Items.SetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowWidth", windowName), windowState.Size.Width, windowState.Size.Width);
                    options.Items.SetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowHeight", windowName), windowState.Size.Height, windowState.Size.Height);
                }
                options.Items.SetValue(string.Format(CultureInfo.InvariantCulture, "{0}.windowState", windowName), windowState.WindowState, windowState.WindowState, _converter);
            });
        }

        protected virtual Type WorkItemType { get { return typeof(WorkItem); } }

        protected virtual void Initialize() { }


        private void OnNotifyOpen()
        {
            if (NotifyOpen != null)
            {
                NotifyOpen(this, new PluginWindowEventArgs(this.Name));
            }
        }
        private void OnNotifyClose()
        {
            if (NotifyClose != null)
            {
                NotifyClose(this, new PluginWindowEventArgs(this.Name));
            }
        }

        private IPluginWorkItem _workItem;

        private class WorkItemData { }
        private class WorkItem : PluginWindowWorkItem<WorkItemData, PluginWindow>
        {
            public WorkItem(PluginWindow window) : base(window) { }
        }
        protected void OnStart()
        {
            if (CanStart)
            {
                Start();
            }
        }
        protected void OnStop()
        {
            if (CanStop)
            {
                Stop();
            }
        }
        protected void OnReset()
        {
            if (CanReset)
            {
                ResetWorkItem();
            }
        }
        public IPluginWorkItem Item { get { return _workItem; } }

        protected virtual bool CanStart { get { return false; } }
        protected virtual bool CanStop { get { return false; } }
        protected virtual bool CanReset { get { return false; } }

        public bool ShowToolStrip
        {
            get
            {
                return _showToolStrip;
            }
            set
            {
                _showToolStrip = value;
                ProcessControl(_showToolStrip, this.toolStrip1);
            }
        }

        public bool ShowStatusStrip
        {
            get
            {
                return _showStatusStrip;
            }
            set
            {
                _showStatusStrip = value;
                ProcessControl(_showStatusStrip, this.statusStrip1);
            }
        }

        private void ProcessControl(bool value, Control control)
        {
            this.SuspendLayout();
            if (value)
            {
                this.Controls.Add(control);
            }
            else
            {
                this.Controls.Remove(control);
            }
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        public event EventHandler<EventArgs> NotifyStart;
        public event EventHandler<EventArgs> NotifyStop;

        void workItem_Start(object sender, EventArgs e)
        {
            if (NotifyStart != null)
            {
                NotifyStart(this, EventArgs.Empty);
            }
        }
        void workItem_Stop(object sender, EventArgs e)
        {
            if (NotifyStop != null)
            {
                NotifyStop(this, EventArgs.Empty);
            }
        }
        private void Start()
        {
            if (!this._workItem.IsRunAsync)
            {
                this._workItem.RunAsync();
            }
        }
        private void Stop()
        {
            if (this._workItem.IsRunAsync)
            {
                this._workItem.CancelAsync();
            }
        }
        private void ResetWorkItem()
        {
            if (!this._workItem.IsRunAsync)
            {
                Initialize();
            }
        }
        private void startButton_Click(object sender, EventArgs e)
        {
            OnStart();
        }
        private void stopButton_Click(object sender, EventArgs e)
        {
            OnStop();
        }
        private void resetButton_Click(object sender, EventArgs e)
        {
            OnReset();
        }

        public event EventHandler<PluginWindowEventArgs> NotifyLoad;

        public event EventHandler<PluginWindowEventArgs> NotifyUnload;

        private void OnNotifyLoad()
        {
            if (NotifyLoad != null)
            {
                NotifyLoad(this, new PluginWindowEventArgs(this.Name));
            }
        }

        private void OnNotifyUnload()
        {
            if (NotifyUnload != null)
            {
                NotifyUnload(this, new PluginWindowEventArgs(this.Name));
            }
        }


        public PluginWindow()
        {
            this._backgroundProperty = new BackgroundProperty(this);

            InitializeGraphics();
            InitializeComponent();

            LoadColors();
            LoadTool();

            try
            {
                this._workItem = Create();
                this._workItem.Start += new EventHandler<EventArgs>(workItem_Start);
                this._workItem.Stop += new EventHandler<EventArgs>(workItem_Stop);
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
            }
        }

        protected virtual IPluginWorkItem Create()
        {
            return (IPluginWorkItem)Activator.CreateInstance(WorkItemType, this);
        }

        private void LoadTool()
        {
            this._toolPanel = new PluginToolStripController<PluginToolStrip>(this.toolStrip1);
            this._statusPanel = new PluginToolStripController<PluginStatusStrip>(this.statusStrip1);
        }

        protected bool IsWindowResizing
        {
            get
            {
                return _isWindowResizing;
            }
        }

        void PluginWindow_ResizeEnd(object sender, EventArgs e)
        {
            _isWindowResizing = false;
            UnloadWindowState();
        }

        private bool _isWindowResizing;

        void PluginWindow_ResizeBegin(object sender, EventArgs e)
        {
            _isWindowResizing = true;
            LoadWindowState();
        }

        void PluginWindow_Load(object sender, EventArgs e)
        {
            Initialize();
            OnNotifyLoad();
        }
        private void PluginWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            OnNotifyUnload();
            OnStop();
            if (!_closeOnExit)
            {
                if (e.CloseReason == CloseReason.UserClosing)
                {
                    e.Cancel = true;
                    CloseWindow();
                }
            }
        }
        void PluginWindow_VisibleChanged(object sender, EventArgs e)
        {
            if (Visible)
            {
                Host.Execute(host => { host.Attach(this); });
            }
            else
            {
                Host.Execute(host => { host.Detach(this); });
            }
        }

        private void LoadColors()
        {
            BackColor = Color.Black;
            ForeColor = Color.White;
        }

        public IPluginWindowItem ToolBarWindowItems
        {
            get
            {
                return _toolPanel;
            }
        }

        public IPluginWindowItem StatusBarWindowItems
        {
            get
            {
                return _statusPanel;
            }
        }

        private void InitializeGraphics()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        }

        private bool _modalDialigWindow;
        private bool _closeOnExit = true;

        public DialogResult OpenWindow(IWin32Window owner, bool modalDialigWindow, bool closeOnExit)
        {
            this._modalDialigWindow = modalDialigWindow;
            this._closeOnExit = closeOnExit;
            DialogResult result = DialogResult.None;
            if (!this.Visible)
            {
                this.OnNotifyOpen();
                if (!_modalDialigWindow)
                {
                    result = DialogResult.OK;
                    PluginControlHelper.Action<PluginWindow>(this, control => control.Show(owner));
                    return result;
                }
                else
                {
                    PluginControlHelper.Action<PluginWindow>(this, control => result = control.ShowDialog(owner));
                    return result;
                }
            }
            else
            {
                PluginControlHelper.Action<PluginWindow>(this, control => control.Focus());
                return result;
            }
        }
        public bool AutoHide { get { return !_closeOnExit; } }
        public DialogResult OpenWindow(bool modalDialigWindow, bool closeOnExit)
        {
            this._modalDialigWindow = modalDialigWindow;
            this._closeOnExit = closeOnExit;
            DialogResult result = DialogResult.None;
            if (!this.Visible)
            {
                this.OnNotifyOpen();
                if (!_modalDialigWindow)
                {
                    result = DialogResult.OK;
                    PluginControlHelper.Action<PluginWindow>(this, control => control.Show());
                    return result;
                }
                else
                {
                    PluginControlHelper.Action<PluginWindow>(this, control => result = control.ShowDialog());
                    return result;
                }
            }
            else
            {
                PluginControlHelper.Action<PluginWindow>(this, control => control.Focus());
                return result;
            }
        }
        public void CloseWindow()
        {
            if (this.Visible)
            {
                PluginControlHelper.Action<PluginWindow>(this, control => control.Hide());
                this.OnNotifyClose();
            }
        }
    }
}
